﻿using EasyAbp.Abp.Trees;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Entities;
using Volo.Abp.EntityFrameworkCore;
using EFCore.BulkExtensions;

namespace Joy.Erp.BasicArchives.Core
{
    public class TreeArchiveRepository<TDbContext, TEntity> : EfCoreTreeRepository<TDbContext, TEntity>,
        ITreeArchiveRepository<TEntity>
        where TDbContext : IEfCoreDbContext
        where TEntity : class, IArchiveAggregateRoot, ITree<TEntity>
    {
        public TreeArchiveRepository(IDbContextProvider<TDbContext> dbContextProvider) : base(dbContextProvider)
        {
        }

        public override async Task<IQueryable<TEntity>> GetQueryableAsync()
        {
            return ApplyDataFilters((await GetDbSetAsync()).AsQueryable());
        }

        /// <summary>
        /// 批量插入实体（高性能，使用 EFCore.BulkExtensions）
        /// </summary>
        public virtual async Task BulkInsertAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            var entityArray = entities.ToArray();
            if (entityArray.Length == 0) return;

            var dbContext = await GetDbContextAsync();
            cancellationToken = GetCancellationToken(cancellationToken);

            // 使用 EFCore.BulkExtensions 的 BulkInsertAsync 进行高性能批量插入
            // TDbContext 约束为 IEfCoreDbContext，而所有 ABP 的 DbContext 都继承自 DbContext
            // 通过 as 转换确保类型安全
            var efDbContext = dbContext as DbContext;
            if (efDbContext == null)
            {
                throw new InvalidOperationException("DbContext must inherit from Microsoft.EntityFrameworkCore.DbContext");
            }

            await efDbContext.BulkInsertAsync(
                entityArray,
                new BulkConfig
                {
                    SetOutputIdentity = true,      // 设置是否返回自增ID
                    BatchSize = 1000,              // 批次大小
                    PreserveInsertOrder = true,    // 保持插入顺序
                    BulkCopyTimeout = 300          // 超时时间（秒）
                },
                cancellationToken: cancellationToken
            );

            if (autoSave)
            {
                await dbContext.SaveChangesAsync(cancellationToken);
            }
        }

        /// <summary>
        /// 批量更新实体（高性能，使用 EFCore.BulkExtensions）
        /// </summary>
        public virtual async Task BulkUpdateAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            var entityArray = entities.ToArray();
            if (entityArray.Length == 0) return;

            var dbContext = await GetDbContextAsync();
            cancellationToken = GetCancellationToken(cancellationToken);

            // 使用 EFCore.BulkExtensions 的 BulkUpdateAsync 进行高性能批量更新
            var efDbContext = dbContext as DbContext;
            if (efDbContext == null)
            {
                throw new InvalidOperationException("DbContext must inherit from Microsoft.EntityFrameworkCore.DbContext");
            }

            await efDbContext.BulkUpdateAsync(
                entityArray,
                new BulkConfig
                {
                    BatchSize = 1000,              // 批次大小
                    BulkCopyTimeout = 300          // 超时时间（秒）
                },
                cancellationToken: cancellationToken
            );

            if (autoSave)
            {
                await dbContext.SaveChangesAsync(cancellationToken);
            }
        }

        /// <summary>
        /// 批量删除实体（高性能，使用 EFCore.BulkExtensions）
        /// </summary>
        public virtual async Task BulkDeleteAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            var entityArray = entities.ToArray();
            if (entityArray.Length == 0) return;

            var dbContext = await GetDbContextAsync();
            cancellationToken = GetCancellationToken(cancellationToken);

            var efDbContext = dbContext as DbContext;
            if (efDbContext == null)
            {
                throw new InvalidOperationException("DbContext must inherit from Microsoft.EntityFrameworkCore.DbContext");
            }

            await efDbContext.BulkDeleteAsync(
                entityArray,
                new BulkConfig
                {
                    BatchSize = 1000,
                    BulkCopyTimeout = 300
                },
                cancellationToken: cancellationToken
            );

            if (autoSave)
            {
                await dbContext.SaveChangesAsync(cancellationToken);
            }
        }

        public async override Task<TEntity> InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            CheckAndSetId(entity);

            var code = await GetNextChildCodeAsync(entity.ParentId, GetCancellationToken(cancellationToken));

            entity.SetCode(code);
            entity.Level = entity.Level - 1;

            await (await GetQueryableAsync()).Where(x => x.ParentId == entity.ParentId).CheckUniquePropertyAsync(entity);

            await TraverseTreeAsync(entity, entity.Children);

            var dbContext = await GetDbContextAsync();

            var savedEntity = (await dbContext.Set<TEntity>().AddAsync(entity, GetCancellationToken(cancellationToken))).Entity;

            if (autoSave)
            {
                await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
            }

            return savedEntity;
        }
        public override async Task InsertManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            var entityArray = entities.ToArray();

            foreach (var entity in entityArray)
            {
                await (await GetQueryableAsync()).Where(x => x.ParentId == entity.ParentId).CheckUniquePropertyAsync(entity);
                await InsertAsync(entity, autoSave, cancellationToken);
            }
        }

        //todo: not allow modify children
        public async override Task<TEntity> UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            var oldEntity = await (await GetQueryableAsync()).AsNoTracking().Where(x => x.Id == entity.Id).SingleOrDefaultAsync(cancellationToken);

            if (oldEntity.ParentId == entity.ParentId)
            {
                await base.UpdateAsync(entity, autoSave, cancellationToken);
                return entity;
            }
            //do move
            var parentId = entity.ParentId;
            //Should find children before Code change
            var children = await GetChildrenAsync(entity.Id, true, cancellationToken: GetCancellationToken(cancellationToken));

            //Store old code of Tree
            var oldCode = (oldEntity as ITree<TEntity>).Code;

            //Move Tree
            var code = await GetNextChildCodeAsync(parentId, GetCancellationToken(cancellationToken));
            entity.SetCode(code);
            entity.Level = entity.Level - 1;

            //Update Children Codes
            foreach (var child in children)
            {
                var childCode = TreeCodeGenerator.Append((entity as ITree<TEntity>).Code, TreeCodeGenerator.GetRelative((child as ITree<TEntity>).Code, oldCode));
                child.SetCode(childCode);
                child.Level = child.Level - 1;
            }

            await (await GetQueryableAsync()).Where(x => x.ParentId == entity.ParentId).CheckUniquePropertyAsync(entity);

            var dbContext = await GetDbContextAsync();

            dbContext.Attach(entity);

            var updatedEntity = dbContext.Update(entity).Entity;

            if (autoSave)
            {
                await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
            }

            return updatedEntity;
        }


        public async Task<long> GetCountAsync(bool includeDetails = false, CancellationToken cancellationToken = default)
        {
            var queryable = includeDetails
                ? await WithDetailsAsync()
                : await GetQueryableAsync();

            return await queryable
               .LongCountAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<long> GetCountAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = false, CancellationToken cancellationToken = default)
        {
            var queryable = includeDetails
                ? await WithDetailsAsync()
                : await GetQueryableAsync();

            return await queryable
                .WhereIf(predicate != null, predicate).LongCountAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<TEntity>> GetPagedListAsync(string filter, int skipCount = int.MaxValue, int maxResultCount = 0, string sorting = null, bool includeDetails = false, CancellationToken cancellationToken = default)
        {
            var queryable = includeDetails
                ? await WithDetailsAsync()
                : await GetQueryableAsync();

            return await queryable
                .WhereIf(filter != null, x => ((IArchiveAggregateRoot)x).Code.StartsWith(filter))
                .OrderByIf<TEntity, IQueryable<TEntity>>(!sorting.IsNullOrWhiteSpace(), sorting)
                .PageBy(skipCount, maxResultCount)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<TEntity>> GetPagedListAsync(Expression<Func<TEntity, bool>> predicate, int skipCount = 0, int maxResultCount = int.MaxValue, string sorting = null, bool includeDetails = false, CancellationToken cancellationToken = default)
        {
            var queryable = includeDetails
                ? await WithDetailsAsync()
                : await GetQueryableAsync();

            return await queryable
                .WhereIf(predicate != null, predicate)
                .OrderByIf<TEntity, IQueryable<TEntity>>(!sorting.IsNullOrWhiteSpace(), sorting)
                .PageBy(skipCount, maxResultCount)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        protected override async Task<string> GetNextChildCodeAsync(Guid? parentId, CancellationToken cancellationToken = default)
        {
            var children = await GetChildrenAsync(
                parentId,
                false,
                false,
                GetCancellationToken(cancellationToken));
            var lastChild = children.LastOrDefault();
            if (lastChild == null)
            {
                var parentCode = parentId != null ? await GetCodeAsync(parentId.Value) : null;
                return TreeCodeGenerator.Append(parentCode, TreeCodeGenerator.Create(1));
            }

            return TreeCodeGenerator.Next((lastChild as ITree<TEntity>).Code);
        }

        protected override async Task TraverseTreeAsync(TEntity parent, ICollection<TEntity> children)
        {
            if (children == null || !children.Any())
            {
                return;
            }
            var index = 0;
            foreach (var c in children)
            {
                if (c.Id == Guid.Empty)
                {
                    EntityHelper.TrySetId(c, () => GuidGenerator.Create());
                }
                var code = TreeCodeGenerator.Append((parent as ITree<TEntity>).Code, TreeCodeGenerator.Create(++index));
                c.SetCode(code);
                c.Level = c.Level - 1;
                TraverseTreeAction?.Invoke(c);
                await TraverseTreeAsync(c, c.Children);
            }
        }
    }
}
